﻿using System;
using System.Xml.Linq;
using NJabber.Stream.Elements;
using NUnit.Framework;
using FakeItEasy;

namespace NJabber.Stream.Elements
{
    [TestFixture]
    public class CompositeElementTest : XmppStreamElementTestBase
    {
        private readonly string contentNamespaceUri = "www.content-uri.org";

        public CompositeElementTest() : base("compositeElement", "element-ns", "prefix")
        { }

        [Test]
        public void AddContentItem_NonZeroContentLength_WritesContentItems()
        {
            CompositeElement element = CreateCompositeElement(Name);
            element.AddContentItem(new XmppStreamElementImpl("child1"));
            element.AddContentItem(new XmppStreamElementImpl("child2"));
            XElement expectedElement = CreateXElement(Name);
            expectedElement.Add(new XElement("child1"), new XElement("child2"));

            VerifyWriteOutput(expectedElement, element);
        }

        [Test]
        public void ContentNamespace_NonEmptyString_RenderXmlnsAttribute()
        {
            CompositeElement element = CreateCompositeElement(Name, contentNamespaceUri);
            element.ContentNamespace = contentNamespaceUri;
            XElement expectedElement = CreateXElement(Name, contentNamespaceUri);
            VerifyWriteOutput(expectedElement, element);
        }


        [Test]
        public void Write_NotEmptyXmlNamespaceAndEmptyContentNamspace_WritesXmlCorrectly()
        {
            CompositeElement element = CreateCompositeElement(Name, XmlNamespace.Uri, XmlNamespace.Name);
            XElement expectedElement = CreateXElement(Name, XmlNamespace.Name, XmlNamespace.Uri);

            VerifyWriteOutput(expectedElement, element);
        }


        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void Constructor_ContentNamspaceWithName_ThrowsException()
        {
            CompositeElement element = CreateCompositeElement(Name);
            element.ContentNamespace = new XmlNamespace(XmlNamespace.Name, XmlNamespace.Uri);
        }

        #region FindElement test

        [Test]
        public void FindElement_ContainsElement_ReturnsCorrectElement()
        {
            CompositeElement element = CreateCompositeElement("element");
            AddChildElements(element, "e1", "e2");

            XmppStreamElement childElement = element.FindElement("e1");

            Assert.AreEqual("e1", childElement.Name);
        }

        [Test]
        public void FindElement_NotContainsElement_ReturnsNull()
        {
            CompositeElement element = CreateCompositeElement("element");
            AddChildElements(element, "e1", "e2");

            XmppStreamElement childElement = element.FindElement("e3");

            Assert.IsNull(childElement);
        }

        [Test]
        public void FindElement_TypeParameterAndContainsElement_ReturnsCorrectElement()
        {
            CompositeElement element = CreateCompositeElement("element");
            AddChildElements(element, "e1", "e2");

            XmppStreamElementImpl childElement = element.FindElement<XmppStreamElementImpl>("e2");

            Assert.AreEqual("e2", childElement.Name);
        }

        [Test]
        public void FindElement_TypeParameterAndNotContainsElement_ReturnsNull()
        {
            CompositeElement element = CreateCompositeElement("element");
            AddChildElements(element, "e1", "e2");

            XmppStreamElementImpl childElement = element.FindElement<XmppStreamElementImpl>("e3");

            Assert.IsNull(childElement);
        }

        private void AddChildElements(CompositeElement element, params string[] childElementsNames)
        {
            foreach (string elementName in childElementsNames)
            {
                element.AddContentItem(new XmppStreamElementImpl(elementName));
            }
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void FindElement_ElementNameNull_ThrowsException()
        {
            CompositeElement element = CreateCompositeElement("e");

            element.FindElement(null);
        }

        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void FincElement_ElementNameIsEmptyString_ThrowsException()
        {
            CompositeElement element = CreateCompositeElement("e");
            element.FindElement(string.Empty);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void FindElement_GenericParameterAndElementNameNull_ThrowsException()
        {
            CompositeElement element = CreateCompositeElement("e");
            element.FindElement<XmppStreamElement>(null);
        }

        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void FindElement_GenericParameterAndElementNameEmptyString_ThrowsException()
        {
            CompositeElement element = CreateCompositeElement("e");
            element.FindElement<XmppStreamElement>(string.Empty);
        }

        #endregion

        [Test]
        public void Equals_SameChildSet_ReturnsTrue()
        {
            CompositeElement<string> element1 = CreateCompositeElement<string>(Name);
            CompositeElement<string> element2 = CreateCompositeElement<string>(Name);

            element1.AddContentItems("child1", "child2");
            element2.AddContentItems("child1", "child2");

            Assert.That(element1.Equals(element2), Is.True);
        }

        [Test]
        public void Equals_DifferentChildSet_ReturnsFalse()
        {
            CompositeElement<int> element1 = CreateCompositeElement<int>(Name);
            CompositeElement<int> element2 = CreateCompositeElement<int>(Name);

            element1.AddContentItems(0, 1);
            element2.AddContentItems(2, 3);

            Assert.That(element1.Equals(element2), Is.False);
        }

        private CompositeElement CreateCompositeElement(string name, string namespaceUri = "", string namespaceName = "")
        {
            return CreateStreamElementMock<CompositeElement>(name, namespaceUri, namespaceName);
        }

        private CompositeElement<T> CreateCompositeElement<T>(string name, string namespaceUri = "", string namespaceName = "")
        {
            return CreateStreamElementMock<CompositeElement<T>>(name, namespaceUri, namespaceName);
        }
    }
}
